/*
 * sysmo 服务器主控模块
 * Copyright (C) 2009  YuLicheng
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.sysmo.server;

import org.sysmo.server.user.*;
import org.sysmo.server.node.*;
import java.io.IOException;
import java.util.List;
import java.util.Vector;
import org.sysmo.db.DatabaseInterface;

/**
 *
 * sysmo 服务器的主控模块，sysmo 服务器从这里启动。
 * <br />
 * 此模块整合整个sysmo系统的其他部分，包括监听客户端连接、提供Web用户
 * 数据读取接口以及客户端的远程部署配置等。
 * <br />
 *
 * 在Java EE应用服务器部署sysmo时，应在应用服务器启动时同时实例化此模块。
 * 可用一个Servlet，在其web.xml配置中此Servlet的下增加属性
 * <code>&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;</code>，
 * 然后在此Servlet的代码中重载init方法，并在此方法中创建一个新线程，
 * 并且在新线程中实例化本模块。
 * <br />
 *
 * 由于sysmo服务器本身可独立于应用服务器运行，而数据库访问通常可交由
 * 应用服务器或使用应用服务器的框架托管，因此实例化此模块时，
 * 需要给构造方法传入指定的数据库接口实现。
 * <br />
 * 
 * 另外还需要给出服务器的配置文件名，使得服务器能够按照配置执行初始化。
 * <br />
 *
 * 在实例化此模块后，可以立即调用{@link #doServerLoop() }方法将控制
 * 权交给sysmo 服务器。
 * <br />
 *
 * 注意：对于被监控机器列表，可从此类中获取，后续的信息（如机器上的监控项）
 * 则使用获取的机器实例中的相应方法获取。某些其他元数据配置的获取和更改
 * 直接通过相应的<code>org.sysmo.server.config</code>包内的对象进行操作。
 * <br />
 *
 * @author YuLicheng
 * @see #SysmoServer(DatabaseInterface, String)
 * @see DatabaseInterface
 */
public class SysmoServer implements NodeStateChangeListener {

    private DatabaseInterface dbInterface;
    private String configFileName;
    private Vector<NewNode> newNodes = new Vector<NewNode>();
    private Vector<AddedNode> addedNodes = new Vector<AddedNode>();

    /**
     * 构造方法中包含了sysmo 服务器启动时需要执行的一些初始化动作，主要包括
     * 服务器配置文件的读取，设置各参数项等操作。
     * <br />
     *
     * 构造方法调用后即服务器启动完成。之后必须调用
     * {@link #doServerLoop()}方法将控制权交给sysmo 服务器，这样才能使得
     * 服务器能够监听和处理
     * 各客户端连接。
     * 
     * @param dbInterface 数据库存储接口。
     * @param configFileName 配置文件文件名。
     * @throws IOException 配置文件读取错误时抛出异常。
     * @see #doServerLoop()
     * @see DatabaseInterface
     */
    public SysmoServer(DatabaseInterface dbInterface, String configFileName)
            throws IOException {
        this.dbInterface = dbInterface;
        this.configFileName = configFileName;
    }

    /**
     * 此方法是sysmo 服务器的控制主循环。
     * <br />
     *
     * 当sysmo 服务器初始化完成后，必须调用此方法来将控制权交给sysmo 服务器。
     * 此方法中，sysmo服务器将等待客户端连接，并且处理已连接的客户端，或者
     * 与客户端相互传递消息。
     * <br />
     *
     * 当有已加入监控客户端连接时，此方法会实例化相应的{@link ConnectedNode}
     * 对象，并调用{@link ConnectedNode#connect()}方法将此客户端节点的后续动作
     * 交给此实例处理后继续监听新连接。
     * <br />
     *
     * 当有新的客户端节点连接时，此方法实例化相应的{@link NewNode}对象，并调用
     * {@link NewNode#waitConnect()}方法将此新客户端相应的控制权转交给此实例
     * 处理后继续监听新连接。
     *
     * 此方法从不返回，除非{@link #shutdown()}方法被调用。{@link #shutdown()}
     * 方法会设置停止运行标志导致此方法结束并返回。具体请参看此方法的说明。
     *
     * @see #SysmoServer(DatabaseInterface, String)
     * @see ConnectedNode#connect()
     * @see NewNode#waitConnect()
     */
    public void doServerLoop() {
        /*
         * Note: 去除下面代码的注释可测试sysmo 服务器的运行情况。
         * 这些代码只做测试用，最后会被替换为有实际作用的代码。
         */
        /*System.err.println(">>doServerLoop!!!");
        long m = System.currentTimeMillis();
        while (true) {
            if (System.currentTimeMillis() - m >= 2000) {
                m = System.currentTimeMillis();
                System.out.println(">>Tick:" + m);
            }
        }*/
    }

    /**
     * 关闭服务器。当sysmo 服务器端需要关闭时，此方法被调用。
     * <br />
     *
     * 此方法首先会使{@link #doServerLoop()}方法结束返回，使其不会产生新的连接
     * 会给所有已连接的和等待加入的客户端节点发送结束消息，等待所有连接断开后返回，
     * 从而可安全地停止并销毁服务器实例。
     *
     * <br />
     * 注意：客户端在得知服务器断开后仍会持续尝试连接，使得当服务器恢复运行后，
     * 其能够立即和服务器恢复通信，因此在断开客户端连接前必须先停止监听，否则可能
     * 导致已经断开的客户端重新连入服务器。
     */
    public void shutdown() {
        
    }

    /**
     * 通过用户名获得用户对象实例。此用户应已存在（已在服务器上注册）。
     * <br />
     *
     * 服务器主模块只提供通过用户名获得一个实例方法，其他所有的对于此用户
     * 或其他注册用户的操作都可通过此用户的{@link User}对象实例进行。
     *
     * <br />
     * 此类实例中并不保存所有用户列表，而是每次需要得到一个用户实例时都重新
     * 创建一个新实例，在一个用户的一次登录会话中，由外部的控制逻辑来保存
     * 获得的实例。由于用户登录频率不高，这样的做法不会过多占用服务器内存，
     * 也不至于明显影响性能。
     *
     * @param name 用户名。
     * @return 对应用户名的对象实例。
     * @throws UserNotExistsException 用户名不存在时抛出此异常。
     * @see User
     */
    public User getUser(String name) throws UserNotExistsException {
        return new User(dbInterface, name);
    }

    /**
     * 根据节点ID得到某个节点实例。此方法首先在已连接列表中查找相应的节点，若已连接
     * 则返回{@link ConnectedNode}对象实例，否则返回{@link UnconnectedNode}对
     * 象实例。
     *
     * @param nodeID 节点ID。
     * @return 此节点实例。
     * @throws NodeNotExistsException 若节点ID不存在，则抛出此异常。
     */
    public AddedNode getNode(int nodeID) throws NodeNotExistsException {
        return null;
    }

    /**
     * 返回监控列表中的所有节点。
     * <br />
     *
     * 注意此列表内包含监控列表内记录的所有记录，包括已连接的和未连接的节点。此方
     * 法返回的{@link AddedNode}中若已连接，则实际为{@link ConnectedNode}，
     * 否则为{@link UnconnectedNode}。
     *
     * @return 所有节点列表。
     * @see AddedNode#isConnected()
     */
    public List<AddedNode> getAllNodes() {
        return addedNodes;
    }

    /*
     * 以下两个方法为NodeStateChangeListener中要求实现的方法。
     * 当有节点被添加或删除时这两个方法会被调用，sysmo 服务器
     * 核心即可将相应的节点在等待添加列表和监控列表之间迁移或
     * 删除。
     */
    public void nodeAdded(NewNode oldNode, ConnectedNode newNode) {
        
    }

    public void nodeRemoved(AddedNode node) {
        
    }

    public void nodeDisconnected(ConnectedNode node, int mode) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /**
     * 返回所有等待加入监控列表的新节点。
     *
     * @return 所有新节点列表。
     */
    public List<NewNode> getAllNewNodes() {
        return newNodes;
    }
    
    private ConnectedNode acceptClientConnection() {
        return null;
    }

}
